Google DocsのようなCollaborationを実現するGoogle Realtime API
Video
とりあえずこれを観る daiiz.icon
https://youtu.be/hv14PTbkIs0
Google Docs Style instant collaboration
全部JS, サーバ不要
コンフリクト全自動解消!!
バックエンドはGoogle Drive
Realtime cube
システム構築
https://gyazo.com/c6b374e956261bbc6bec4b77799b68d7
ユーザが作るもの
Realtime data model
(only) your application!
Working with the data model
gaip.drive.realtime.load(fileId, onFileLoaded, initModel)
onFileLoadedでevent listenerを仕掛ける
作例
ダイアグラム、フローチャート作成アプリ
裏側に迫る
https://gyazo.com/4291f9d4d9721d09040a997f68ed89eb
リアルタイム編集
大量の編集差分リクエストを受け付けて、全てのクライアント上のドキュメントで整合性を保たなければいけない
とにかくすべてのmutaionsを保存しておく
append only
insert
指定位置 (index) に文字列を挿入する
https://gyazo.com/456d46b72dbc619cbfdacb73b0fa0cd7
delete
指定位置から何文字かを消す
https://gyazo.com/98f77117e3d693174fa26c17bfa7c317
サーバにすべてのmutationをstoreしておく
ドキュメントが読み込まれたら、これらのmutationからビルドされるsnapshotを送信する https://gyazo.com/9d165d8e440492c61dd1732f664cda60
複数人での同期を図る仕組み
mutationを文字通りそのまま受け入れていたら自分や他人の編集が衝突しまくり、ドキュメントが壊れる
思考実験
https://gyazo.com/ba0aa06b51fbfd0223141c14bcfa2364
Aさんが index=4 の位置に5文字 insert
NG: Aさんのmutationを受け入れ前に、Bさんが index=14 の位置に文字列を追加
OK: Aさんのmutationを受け入れ前に、Bさんが index=20 の位置に文字列を追加
直前までの変化を汲んで適用するmutationを読み替えていかないといけない
Story1. サーバでの変換処理 Transformation
1. A -> Google: insert @4 rainy
2. B (locally changed): insert @14 brightly
3. B -> Google: insert @14 brightly
4. Google -> B (locally merged) : insert @4 rainy (... A's mutation)
5. Google -> A (locally merged) : insert @20 brightly (... modified B's mutation)
https://gyazo.com/066cdf29f91dfa66b47fa44a89c4d4d8
Story2. サーバはすべてのchangesは変換できない
https://gyazo.com/ab77b36d50d070ac98bf501ed714303c
The server might not know about A's mutation after it send B's to A.
1. B -> Google: *insert @14 6-char: 一番乗りのcommitなので、serverがmodifyする必要ない
2. A (locally changed): **insert @4 5-char
この変更はまだserverは知らない
3. Google -> A: * (... B's commit)
衝突するので、Aが見ているドキュメントが壊れる
4. A -> Google: ** (... A's commit)
サーバでは整合性がとれている (変換の必要なし)
5. Google -> B: ** (... A's commit)
Bが見ているドキュメントは整合性が取れている
Story2の解決策
サーバでだけでなく、Clientでもtransformする
Keep a client-side queue of sent mutations and transform incoming mutations against the queue
送信済みのcommitsのキューを持っておく
受信しているcommitsをキューの内容に対して変換する
各々のクライアントでTransform Managerというやつを用意する
Pending Mutation Queue 送信待ちキュー
Story2での Step2 の**insert @4 5-charがここに居る
サーバーからcommitを受け取る
もちろん、このcommitは、クライアントのPending Mutaion Queueとの衝突は考慮していない (できるはずがないので当然の状態)
Transform Manager内で自身に適用するmodified commitを作成する
https://gyazo.com/cdcb4d2f50ecbd2b086bb9c0a1de34ce
サーバからACKを返されたら、Pending Queueの中からcommitを消去できる
https://gyazo.com/00121d11eb11b0309144329cf8676846
Undoの仕組み
自分がやったmutationを取り消して元に戻す仕組み
あるmutationを確定した際に、Undo Stackに inverse mutation (真逆のcommit) を積んでいく
insertを確定したならば、指定位置から指定文字数分消去する delete commit を積む
https://gyazo.com/62bf6abad071f4fd329df566e0ec8780
これだと、他人に編集されると打ち消し位置が壊れる
inverse mutationの他に、すべての共同編集者から受信したmutationsを積んでいく
なるほどdaiiz.icon*2
これでさっきと同じTransform Functionを使えそう
https://gyazo.com/e7e72d139422b6bee029ddfa62f2674e
共同編集者の編集情報を考慮した上で、自分の操作を取り消せる
取り消し開始位置をshiftして対処できる
https://gyazo.com/5d62bd2317f2dad624c8371c72ef5f7f
例
Event planning system を作りながら、data model の構築を学ぶ
意味のある複数のプロパティをひとかたまりに定義できる
各々のプロパティを到着順に解決していると、サーバへの到着順によっては、days, 20 のような組み合わせになる可能性がある
トランザクション定義のようなものか
https://gyazo.com/4ae02f4624e35ee75ca2a50c1157e654
一つのtaskを誰かに割り当てるための操作は2ステップ
taskを古い所属から消す、新しい所属にtaskを追加する
うまいことやらないと、新しい所属先が2個同時に設定されたりする
https://gyazo.com/ba59415db114ff5c7c4d7e560e726e1f
Export data model as JSON https://gyazo.com/a470ba17116ba2fd4e0bf437302bc969
逆に、JSONをアップロードすると、Model Diff を抽出して更新してくれる